﻿namespace Neovolve.Extensibility.ReSharper
{
    using System;
    using System.Linq;
    using JetBrains.DocumentModel;
    using JetBrains.ProjectModel;
    using JetBrains.ReSharper.Psi;
    using JetBrains.ReSharper.Psi.CSharp.Parsing;
    using JetBrains.ReSharper.Psi.CSharp.Tree;
    using JetBrains.ReSharper.Psi.Tree;
    using JetBrains.Text;
    using JetBrains.Util;

    /// <summary>
    /// The <see cref="NodeDescriptor"/>
    ///   class is used to provide node type detection and conversion logic.
    /// </summary>
    internal class CommentNodeDescriptor : NodeDescriptor
    {
        /// <summary>
        /// Stores the type name in the comment.
        /// </summary>
        private readonly String _typeName;

        /// <summary>
        /// Initializes a new instance of the <see cref="CommentNodeDescriptor"/> class.
        /// </summary>
        /// <param name="node">
        /// The node being described.
        /// </param>
        /// <param name="commentCaretIndex">
        /// Index of the comment caret.
        /// </param>
        /// <param name="typeName">
        /// Name of the type.
        /// </param>
        public CommentNodeDescriptor(IDocCommentNode node, Int32 commentCaretIndex, String typeName)
            : base(node, false)
        {
            _typeName = typeName;
            CommentCaretIndex = commentCaretIndex;

            AnalyzeNode();
        }

        /// <summary>
        /// Creates the new comment node.
        /// </summary>
        /// <param name="commentText">
        /// The comment text.
        /// </param>
        /// <returns>
        /// A <see cref="ITreeNode"/> instance.
        /// </returns>
        internal static ITreeNode CreateNewCommentNode(String commentText)
        {
            StringBuffer buffer = new StringBuffer(commentText);
            TreeOffset endOffset = new TreeOffset(commentText.Length);

            return CSharpTokenType.END_OF_LINE_COMMENT.Create(buffer, TreeOffset.Zero, endOffset);
        }

        /// <summary>
        /// Creates the new comment text.
        /// </summary>
        /// <param name="commentText">
        /// The comment text.
        /// </param>
        /// <param name="newTypeName">
        /// New name of the type.
        /// </param>
        /// <returns>
        /// A <see cref="String"/> instance.
        /// </returns>
        internal String CreateNewCommentText(String commentText, String newTypeName)
        {
            return commentText.Substring(0, CommentCaretIndex) + newTypeName + commentText.Substring(CommentCaretIndex + _typeName.Length);
        }

        /// <summary>
        /// Gets the comment text.
        /// </summary>
        /// <returns>
        /// A <see cref="String"/> instance.
        /// </returns>
        internal String GetCommentText()
        {
            return Node.GetText();
        }

        /// <summary>
        /// Gets the document range.
        /// </summary>
        /// <returns>
        /// A <see cref="DocumentRange"/> value.
        /// </returns>
        protected internal override DocumentRange GetDocumentRange()
        {
            Int32 startIndex = Node.GetTreeStartOffset().Offset + CommentCaretIndex;
            TextRange textRange = new TextRange(startIndex, startIndex + _typeName.Length);
            IProjectFile projectItem = Node.GetProjectFile();

            if (projectItem == null)
            {
                return DocumentRange.InvalidRange;
            }

            DocumentRange range = new DocumentRange(projectItem, textRange);

            return range;
        }

        /// <summary>
        /// Creates the name of the node for type.
        /// </summary>
        /// <param name="typeName">
        /// Name of the type.
        /// </param>
        /// <returns>
        /// A <see cref="ITreeNode"/> value.
        /// </returns>
        protected override ITreeNode CreateNodeForTypeName(String typeName)
        {
            return CreateNewCommentNodeForTypeName(typeName);
        }

        /// <summary>
        /// Populates the node information.
        /// </summary>
        protected override void PopulateNodeInformation()
        {
            AliasTypeMapping clrTypeMapping = NodeAnalyser.AliasTypeMappings.Where(x => x.ClrTypeName == _typeName).FirstOrDefault();

            if (clrTypeMapping != null)
            {
                IsClrType = true;
                TypeMapping = clrTypeMapping;
            }
            else
            {
                // See if this type name exists as an alias type
                AliasTypeMapping aliasTypeMapping = NodeAnalyser.AliasTypeMappings.Where(x => x.AliasTypeName == _typeName).FirstOrDefault();

                if (aliasTypeMapping != null)
                {
                    IsClrType = false;
                    TypeMapping = aliasTypeMapping;
                }
            }
        }

        /// <summary>
        /// Creates the new comment node.
        /// </summary>
        /// <param name="newTypeName">
        /// New name of the type.
        /// </param>
        /// <returns>
        /// A <see cref="ITreeNode"/> value.
        /// </returns>
        private ITreeNode CreateNewCommentNodeForTypeName(String newTypeName)
        {
            String commentText = GetCommentText();
            String newNodeText = CreateNewCommentText(commentText, newTypeName);
            return CreateNewCommentNode(newNodeText);
        }

        /// <summary>
        /// Gets the index of the comment caret.
        /// </summary>
        /// <value>
        /// The index of the comment caret.
        /// </value>
        public Int32 CommentCaretIndex
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets a value indicating whether the node is supported by this descriptor.
        /// </summary>
        /// <value>
        /// <c>true</c> if the node is supported by this descriptor; otherwise, <c>false</c>.
        /// </value>
        public override Boolean IsSupportedNode
        {
            get
            {
                return Node is IDocCommentNode;
            }
        }
    }
}